Skip to content

perf: optimize regexp_instr (40% faster)#23540

Merged
alamb merged 3 commits into
apache:mainfrom
andygrove:auto-opt/regexp_instr-datafusion-20260713-155538
Jul 17, 2026
Merged

perf: optimize regexp_instr (40% faster)#23540
alamb merged 3 commits into
apache:mainfrom
andygrove:auto-opt/regexp_instr-datafusion-20260713-155538

Conversation

@andygrove

@andygrove andygrove commented Jul 13, 2026

Copy link
Copy Markdown
Member

Which issue does this PR close?

N/A

Rationale for this change

Optimize existing expression.

What changes are included in this PR?

Removed per-batch default-array/Vec allocations, memoized the compiled regex across rows to skip HashMap hashing for literal patterns, and skipped the O(n) chars() scan on the default start=1 path.

Are these changes tested?

Existing tests.

Benchmark (criterion):

  • regexp_instr_with_start [size=1024, str_len=32]: 40.073% faster (base 41127ns -> cand 24646ns)
  • regexp_instr_with_start [size=1024, str_len=128]: 44.498% faster (base 48215ns -> cand 26760ns)
  • regexp_instr_no_start [size=1024, str_len=128]: 47.253% faster (base 49244ns -> cand 25974ns)
  • regexp_instr_no_start [size=1024, str_len=32]: 42.18% faster (base 40495ns -> cand 23414ns)

Full criterion output:

regexp_instr_no_start [size=1024, str_len=32]
                        time:   [23.389 µs 23.479 µs 23.645 µs]
                        change: [−42.300% −42.180% −42.004%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 9 outliers among 100 measurements (9.00%)
  7 (7.00%) low mild
  2 (2.00%) high severe

regexp_instr_with_start [size=1024, str_len=32]
                        time:   [24.617 µs 24.635 µs 24.659 µs]
                        change: [−40.118% −40.073% −40.026%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high severe

regexp_instr_no_start [size=1024, str_len=128]
                        time:   [25.756 µs 25.791 µs 25.830 µs]
                        change: [−47.394% −47.253% −47.076%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high severe

regexp_instr_with_start [size=1024, str_len=128]
                        time:   [26.645 µs 26.666 µs 26.693 µs]
                        change: [−44.577% −44.498% −44.417%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high mild

Are there any user-facing changes?

No

@github-actions github-actions Bot added the functions Changes to functions implementation label Jul 13, 2026
@andygrove andygrove changed the title perf: optimize regexp_instr in datafusion-functions perf: optimize regexp_instr (40% faster) Jul 14, 2026
@andygrove
andygrove marked this pull request as ready for review July 14, 2026 15:57
};
let pattern: &Regex = compile_and_cache_regex(pattern, flags, regex_cache)?;
// println!("get_index: value = {}, pattern = {}, start = {}, n = {}, subexpr = {}, flags = {:?}", value, pattern, start, n, subexpr, flags);
) -> Result<i64, ArrowError> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should it return -1 if there is no match? 0 sounds like legit index

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR doesn't make any functional changes. The index is 1-based, I believe.

@alamb alamb added the performance Make DataFusion faster label Jul 15, 2026
}

fn criterion_benchmark(c: &mut Criterion) {
let size = 1024;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think most batches are 8k not 1k

@alamb alamb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @andygrove -- this looks good to me

While reviewing I also found a few other cases that I think are worth testing; I made a separate PR for them here:

use std::hint::black_box;
use std::sync::Arc;

fn create_args<O: OffsetSizeTrait>(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW @andygrove -- it would make it easier to verify these results if you made one PR to add the benchmark and then a second PR to make the code change

That way we can use the run benchmark <benchmark> bot command to get a second performance run

Ok(Some(match_start_char_offset))
} else {
Ok(Some(0)) // Return 0 if the N-th match was not found
/// Compiles the patterns seen so far, keyed by `(pattern, flags)`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks similar to https://github.com/apache/datafusion/blob/998f534aafbf55c83daaa6fd4985ba143954b0e0/datafusion/functions/src/regex/mod.rs#L131-L130

Maybe we could (as a follow on PR) extract the common regexp caching functionality) -- I personally prefer this Cache pattern -- for use in the other functions with regular expressions

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I filed #23675. I will work on this once this PR merges.

@alamb

alamb commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

I merged up to resolve the conflicts

@alamb
alamb enabled auto-merge July 17, 2026 19:22
tohuya6 pushed a commit to tohuya6/datafusion that referenced this pull request Jul 17, 2026
…pache#23620)

## Which issue does this PR close?

N/A (found while reviewing test coverage for apache#23540)

## Rationale for this change

While checking `cargo llvm-cov` coverage of `regexp_instr`, I found that
the two `statement error` tests for `start < 1` in `regexp_instr.slt`
never actually exercise the start validation.

Coverage also showed two paths of `regexp_instr` untested anywhere:

- the `N must be 1 or greater` error for `nth < 1`
- looking up an already-compiled regex from the per-batch cache when the
pattern column returns to a previously seen pattern (e.g. `['abc',
'def', 'abc']`). This path becomes more important with the memoization
added in apache#23540.

## What changes are included in this PR?

- Fix the two malformed `statement error` tests so the expected error is
matched inline against the real message
- Add a `statement error` test for `nth < 1`
- Add a test with a pattern column that alternates between two regexes
within a single batch

## Are these changes tested?

Yes, this PR is only tests. 

## Are there any user-facing changes?

No

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@alamb
alamb added this pull request to the merge queue Jul 17, 2026
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.11765% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.65%. Comparing base (4957f5d) to head (2b6a751).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
datafusion/functions/src/regex/regexpinstr.rs 94.11% 1 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #23540      +/-   ##
==========================================
- Coverage   80.66%   80.65%   -0.01%     
==========================================
  Files        1087     1087              
  Lines      367409   367380      -29     
  Branches   367409   367380      -29     
==========================================
- Hits       296367   296321      -46     
- Misses      53390    53400      +10     
- Partials    17652    17659       +7     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Merged via the queue into apache:main with commit 4c20fa7 Jul 17, 2026
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

functions Changes to functions implementation performance Make DataFusion faster

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants